<?php
namespace Shopiuu\Http;

class RequestCore{
    public $request_headers;
    public $request_proxy = '';
    public $request_user_agent = 'Shopiuu Api-sdk-php 1.0';
    public $request_referer = '';
    public $request_cookies = '';
    public $request_body = '';
    public $request_method = 'POST';
    public $request_url = '';
    /**
     * The request timeout time, which is 5,184,000 seconds,that is, 6 days by default
     *
     * @var int
     */
    public $timeout = 5184000;

    /**
     * The connection timeout time, which is 30 seconds by default
     *
     * @var int
     */
    public $connect_timeout = 30;
    /**
     * The raw response callback headers
     */
    public $response_raw_headers;

    /**
     * Response body when error occurs
     */
    public $response_error_body;
    /**
     * The response returned by the request.
     */
    public $response;

    /**
     * The headers returned by the request.
     */
    public $response_headers;

    /**
     * The body returned by the request.
     */
    public $response_body;

    /**
     * The HTTP status code returned by the request.
     */
    public $response_code;

    /**
     * Additional response data.
     */
    public $response_info;

    public function __construct($url = null, $proxy = null)
    {
        // Set some default values.
        $this->request_url = $url;
        //$this->request_method = self::HTTP_GET;
        $this->request_headers = array();
        $this->request_body = '';

        if ($proxy) {
            $this->set_proxy($proxy);
        }

        return $this;
    }

    public function set_url($url)
    {
        $this->request_url = $url;
        return $this;
    }

    public function set_proxy($proxy)
    {
        $this->request_proxy = $proxy;
        return $this;
    }

    public function set_user_agent($user_agent)
    {
        $this->request_user_agent = $user_agent;
        return $this;
    }

    public function set_referer($referer)
    {
        $this->request_referer = $referer;
        return $this;
    }

    public function set_cookies($cookies)
    {
        $this->request_cookies = $cookies;
        return $this;
    }

    public function set_body($body)
    {
        $this->request_body = $body;
        return $this;
    }

    public function set_method($method)
    {
        $this->request_method = $method;
        return $this;
    }

    public function add_header($key, $value)
    {
        $this->request_headers[$key] = $value;
        return $this;
    }
    
    public function pre_request()
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->request_url);
        if ($this->request_proxy)
        {
            curl_setopt($ch, CURLOPT_PROXY, $this->request_proxy);
        }
        curl_setopt($ch, CURLOPT_HEADER, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        if ($this->request_user_agent)
        {
            curl_setopt($ch, CURLOPT_USERAGENT, $this->request_user_agent);
        }
        if ($this->request_referer)
        {
            curl_setopt($ch, CURLOPT_REFERER, $this->request_referer);
        }
        if ($this->request_cookies)
        {
            curl_setopt($ch, CURLOPT_COOKIE, $this->request_cookies);
        }
        if (isset($this->request_headers) && count($this->request_headers)) {
            $temp_headers = array();

            foreach ($this->request_headers as $k => $v) {
                $temp_headers[] = $k . ': ' . $v;
            }

            // fix "Expect: 100-continue"
            $temp_headers[] = 'Expect:';
            curl_setopt($ch, CURLOPT_HTTPHEADER, $temp_headers);
        }
        if($this->request_method)
        {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST,$this->request_method);
        }
        if ($this->request_body)
        {
            //var_dump($post_data);
            switch($this->request_method)
            {
                case 'PUT':
                case 'DELETE':
                    curl_setopt($ch, CURLOPT_POSTFIELDS, $this->request_body);
                    break;
    
                case 'POST':
                    curl_setopt($ch, CURLOPT_POST, 1);
                    curl_setopt($ch, CURLOPT_POSTFIELDS, $this->request_body);
                    break;
                default:
                    curl_setopt($ch, CURLOPT_POST, 1);
                    curl_setopt($ch, CURLOPT_POSTFIELDS, $this->request_body);
                    break;
            }
        }
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connect_timeout);
        return $ch;
    }

    /**
     * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the
     * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via
     * parameters.
     *
     * @param resource|\CurlHandle|null|false $curl_handle (Optional) The reference to the already executed cURL request. Receive CurlHandle instance from PHP8.0
     * @param string $response (Optional) The actual response content itself that needs to be parsed.
     * @return ResponseCore A <ResponseCore> object containing a parsed HTTP response.
     */
    public function process_response($curl_handle = null, $response = null)
    {
        // Accept a custom one if it's passed.
        if ($curl_handle && $response) {
            $this->response = $response;
        }

        // As long as this came back as a valid resource or CurlHandle instance...
        if (is_resource($curl_handle) || (is_object($curl_handle) && get_class($curl_handle) === 'CurlHandle')) {
            // Determine what's what.
            $header_size = curl_getinfo($curl_handle, CURLINFO_HEADER_SIZE);
            $this->response_headers = substr($this->response, 0, $header_size);
            $this->response_body = substr($this->response, $header_size);
            $this->response_code = curl_getinfo($curl_handle, CURLINFO_HTTP_CODE);
            $this->response_info = curl_getinfo($curl_handle);
            
            if (intval($this->response_code) / 100 != 2 && isset($this->write_file))
            {
                $this->response_headers = $this->response_raw_headers;
                $this->response_body = $this->response_error_body;
            }

            // Parse out the headers
            $this->response_headers = explode("\r\n\r\n", trim($this->response_headers));
            $this->response_headers = array_pop($this->response_headers);
            $this->response_headers = explode("\r\n", $this->response_headers);
            array_shift($this->response_headers);

            // Loop through and split up the headers.
            $header_assoc = array();
            foreach ($this->response_headers as $header) {
                $kv = explode(': ', $header);
                $header_assoc[strtolower($kv[0])] = isset($kv[1]) ? $kv[1] : '';
            }

            // Reset the headers to the appropriate property.
            $this->response_headers = $header_assoc;
            $this->response_headers['info'] = $this->response_info;
            $this->response_headers['info']['method'] = $this->method;
            
            if ($curl_handle && $response) {
                return new ResponseCore($this->response_headers, $this->response_body, $this->response_code);
            }
        }

        // Return false
        return false;
    }
    
    public function send_request($parse = false)
    {
        set_time_limit(0);
        $ch = $this->pre_request();
        $this->response = curl_exec($ch);

        if ($this->response === false) {
            throw new RequestException('cURL error: ' . curl_error($ch) . ' (' . curl_errno($ch) . ')');
        }

        $parsed_response = $this->process_response($ch, $this->response);

        curl_close($ch);
        unset($ch);

        if ($parse) {
            return $parsed_response;
        }

        return $this->response;
    }
    
    /*%******************************************************************************************%*/
    // RESPONSE METHODS

    /**
     * Get the HTTP response headers from the request.
     *
     * @param string $header (Optional) A specific header value to return. Defaults to all headers.
     * @return string|array All or selected header values.
     */
    public function get_response_header($header = null)
    {
        if ($header) {
            return $this->response_headers[strtolower($header)];
        }
        return $this->response_headers;
    }

    /**
     * Get the HTTP response body from the request.
     *
     * @return string The response body.
     */
    public function get_response_body()
    {
        return $this->response_body;
    }

    /**
     * Get the HTTP response code from the request.
     *
     * @return string The HTTP response code.
     */
    public function get_response_code()
    {
        return $this->response_code;
    }
}